package at.fpmedv.eclipse.helma.core;

import helma.framework.core.Application;
import helma.framework.core.Prototype;
import helma.framework.core.RequestEvaluator;
import helma.framework.repository.Resource;
import helma.objectmodel.db.DbMapping;
import helma.objectmodel.db.Relation;
import helma.scripting.rhino.RhinoCore;
import helma.scripting.rhino.RhinoEngine;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;

import org.mozilla.javascript.ScriptableObject;

import at.fpmedv.eclipse.helma.util.ResourceContainer;

/**
 * represents an Helma Prototype in the tree view
 * 
 * @author fpm
 * 
 */
public class HelmaPrototypeTreeObject implements ITreeParent {
	
	/**
	 * The helma prototype object
	 */
	Prototype proto;
	
	/**
	 * parent TreeObject mostly an {@link HelmaAppTreeObject}
	 */
	HelmaAppTreeObject parent;
	
	/**
	 * Stores the corresponding Helma parent prototype
	 */
	HelmaPrototypeTreeObject basePrototype; 

	/**
	 * constructor
	 * 
	 * @param proto helma prototype representation
	 * @param parent parent always an app
	 */
	public HelmaPrototypeTreeObject(Prototype proto, HelmaAppTreeObject parent) {
		this.proto = proto;
		this.parent = parent;
	}

	/**
	 * just a setter for the Helma parent prototype
	 * 
	 * @param basePrototype the Treeobject representation of the parent prototype
	 */
	protected void setBasePrototype(HelmaPrototypeTreeObject basePrototype) {
		this.basePrototype = basePrototype;
	}

	/**
	 * if this node has always children because every Prototype in a HopObject
	 */
	public boolean hasChildren() {
		return true;
	}

	/**
	 * returns list of children
	 */
	public ITreeObject[] getChildren() {
		proto.checkForUpdates();
		ArrayList<ITreeObject> result = new ArrayList<ITreeObject>();
		if (basePrototype != null) {
			result = new ArrayList<ITreeObject>(java.util.Arrays.asList(basePrototype.getChildren()));
		}
		// list properties
		// list children
		DbMapping childrenMapping = proto.getDbMapping();
		Relation childrenRelation = childrenMapping.getSubnodeRelation();
		if (childrenRelation != null) {
			DbMapping childrenProto = childrenRelation.getTargetType();
			if (childrenProto != null) {
				result.add((ITreeObject) new CollectionTreeObject(
						"_children", childrenRelation, this));
			}
		}

		String[] mappings = proto.getDbMapping().getPropertyNames();
		for (int i = 0; i < mappings.length; i++) {
			String propertyName = mappings[i];
			DbMapping protoMapping = proto.getDbMapping();
			Relation propertyRelation = protoMapping.getPropertyRelation(propertyName);
			// check for collection
			if (propertyRelation.isVirtual() && propertyRelation.getPropertyMapping() != null) {
				result.add((ITreeObject) new MountPointTreeObject(
						propertyName, propertyRelation, this));
			} else if (propertyRelation.isVirtual()) {
				result.add((ITreeObject) new CollectionTreeObject(
						propertyName, propertyRelation, this));
			} else {
				result.add((ITreeObject) new PropertyTreeObject(
						propertyName, propertyRelation, this));
			}
		}
		// list functions
		Application app = proto.getApplication();
		RequestEvaluator rEvaluator = app.getEvaluator();
		RhinoEngine sEngine = (RhinoEngine) rEvaluator.getScriptingEngine();
		RhinoCore rCore = sEngine.getCore();
		try {
			sEngine.enterContext();
			Object[] prototypeProperties = ((ScriptableObject) rCore.getPrototype(getRawName())).getAllIds();
			for (Object id: prototypeProperties) {
				String propertyName = id.toString();
				// HelmaCorePlugin.getDefault().log(prototypeProperties.get(propertyName).toString());
				result.add((ITreeObject) new CoreFunctionTreeObject(propertyName, this));
			}
			Map prototypeScriptProperties = rCore.getPrototypeProperties(getRawName());
			// filter doublettes functions
			for (Iterator iterResources = prototypeScriptProperties.keySet().iterator(); iterResources.hasNext();) {
				String propertyName = (String) iterResources.next();
				// loop over core functions and remove item if exists
				for (int i = 0; i < result.size(); i++) {
					ITreeObject item = result.get(i);
					if (propertyName.equals(item.getName())) {
						result.remove(item);
						result.add((ITreeObject) new FunctionTreeObject(propertyName, this));
					}
				}
			}
				
			sEngine.exitContext();
		} catch(java.lang.RuntimeException t) {
			HelmaCorePlugin.getDefault().log("Error reading Functions of " + getRawName(), t);
		} catch (IOException e) {
			HelmaCorePlugin.getDefault().log("Error entering Context", e);
		}
		app.releaseEvaluator(rEvaluator);
		// list skins
		for (Iterator iterSkins = proto.getSkinResources(); iterSkins
				.hasNext();) {
			Resource skinResource = (Resource) iterSkins.next();
			result
					.add((ITreeObject) new SkinTreeObject(skinResource,
							this));
		}
		
		// filter doublettes
		for (int i = 0; i < result.size(); i++) {
			ITreeObject item = result.get(i);
			for (int j = i + 1; j < result.size(); j++) {
				ITreeObject item2 = result.get(j);
				if (item.getName().equals(item2.getName())) {
					result.remove(item2);
				}
			}
		}
		return (ITreeObject[]) result
				.toArray(new ITreeObject[result.size()]);
	}

	public Object getAdapter(Class key) {
		return null;
	}

	public String getName() {
		String result = "";
		if (basePrototype != null && !basePrototype.getName().equals("HopObject")) {
			result = ":" + basePrototype.getName();
		}
		return proto.getName() + result;
	}

	public String getRawName() {
		return proto.getName();
	}

	public ITreeParent getParent() {
		return parent;
	}

	@Override
	public String toString() {
		return getName();
	}

	public ResourceContainer getResource() {
		return null;
	}

	public int compare(ITreeObject element, Comparator comparator) {
		return comparator.compare(this.getName(), element.getName());
	}
}
